/*
 * @Author: zhangzhen
 * @Date: 2023-01-03 09:00:50
 * @LastEditors: zhangzhen
 * @LastEditTime: 2023-03-16 10:10:48
 *
 */
import React, {FC, useContext, useRef,useCallback} from 'react';
import {DeleteFilled} from '@ant-design/icons';
import {Popconfirm, Col, Checkbox,message} from 'antd';
import {useDrag, useDrop, DropTargetMonitor} from 'react-dnd';
import {cloneDeep} from 'lodash';
import styles from './styles.module.scss';
import {FormContext} from '../context';
import {ItemTypes} from '../constant';

const EditBox: FC<any> = (props) => {
    const {children, index,  onlyShowForm, checkForm} = props;
    const {
        formConTextValue,
        setFormConTextValue,
        isEditing,
        setCurrentItem,
        setCurrentIndex,
        currentItem,
        layoutParams,

    } = useContext(FormContext);
    const {colSpan,layout} = layoutParams;

    const ref = useRef<HTMLDivElement>(null);

      // 拖拽组件方法
      const moveItem = useCallback(
        (dragIndex: number, hoverIndex: number) => {
            const cloneValue = [...formConTextValue];
            // 拖拽的组件是 Left 组件，则 dragIndex 为 undefined，则此时修改，则此时修改 FormContextValue 中的占位元素的位置即可
            if (dragIndex === undefined) {
                const lessIndex = formConTextValue.findIndex(
                    (item: any) => item.key === 'dragPlaceholder',
                );
                // 删除上一个占位元素
                cloneValue.splice(lessIndex, 1);
                const hoverItem = formConTextValue[hoverIndex];
                // 根据位置生成新的占位元素
                cloneValue.splice(hoverIndex, 0, {
                    ...hoverItem,
                    key: 'dragPlaceholder',
                });
                setFormConTextValue(cloneValue);
            } else {
                // 拖拽的组件是 Main 组件，则 dragIndex 不为 undefined，此时替换 dragIndex 和 hoverIndex 位置的元素

                const dragItem = formConTextValue[dragIndex];
                const hoverItem = formConTextValue[hoverIndex];
                cloneValue.splice(hoverIndex, 1, dragItem);
                cloneValue.splice(dragIndex, 1, hoverItem);
                setFormConTextValue(cloneValue);
            }
        },
        [formConTextValue, setFormConTextValue],
    );

    const [{opacity}, dragRef] = useDrag({
        // type: 自定义一个名称。拖动的 type 和放置的 type 保持一致。
        // item:参数传递。拖动时的数据能够传递到放置区。
        // collect: 收集监听整个拖动事件的状态数据，比如是否正在进行拖动、拖动偏移量等数据。可以通过源代码获取完整的数据。
        // end: 拖动结束时执行的方法。
        // canDrag: 指定当前是否允许拖动。若希望始终允许被拖动，则可以忽略此方法。

        type: ItemTypes.Field,
        item: {index},
        collect: (monitor: any) => ({
            opacity: monitor.isDragging() ? 0.5 : 1,
        }),
        end: () => {
            // const cloneValue = [...formConTextValue];
            // setFormConTextValue(cloneValue);
        },
    });

    const [, dropRef] = useDrop({
        accept: ItemTypes.Field,
        // 当拖拽的元素，和组件重合时候调用
        hover(item: any, monitor: DropTargetMonitor) {
            if (!ref.current) {
                return;
            }
            const dragIndex = item.index;
            const hoverIndex = index;
            // 拖拽元素下标与鼠标悬浮元素下标一致时，不进行操作
            if (dragIndex === hoverIndex) {
                return;
            }
            // 确定屏幕上矩形范围
            // getBoundingClientRect()用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
            const hoverBoundingRect = ref.current!.getBoundingClientRect();
            // 获取中点垂直坐标
            const hoverMiddleY = hoverBoundingRect.height / 2;
            // 确定鼠标位置
            const clientOffset = monitor.getClientOffset() as any;
            // 获取距顶部距离
            const hoverClientY = clientOffset.y - hoverBoundingRect.top;
            /**
             * 只在鼠标越过一半物品高度时执行移动。
             *
             * 当向下拖动时，仅当光标低于50%时才移动。
             * 当向上拖动时，仅当光标在50%以上时才移动。
             *
             * 可以防止鼠标位于元素一半高度时元素抖动的状况
             */
            // 向下拖动
            if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
                return;
            }
            // 向上拖动
            if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
                return;
            }
            // 执行 move 回调函数
            moveItem(dragIndex, hoverIndex);
            /**
             * 如果拖拽的组件为 Left，则 dragIndex 为 undefined，此时不对 item 的 index 进行修改
             * 如果拖拽的组件为 main，则将 hoverIndex 赋值给 item 的 index 属性
             */
            if (item.index !== undefined) {
                item.index = hoverIndex;
            }
        },
    });

    return (
        <Col  flex={
            formConTextValue[index].key === 'table'
                ? '100%'
                : `${formConTextValue[index].colSpan || 100}%`
        }>
            {!onlyShowForm && !checkForm && isEditing && (
                <div
                    ref={dragRef(dropRef(ref)) as any}
                    style={
                        colSpan === 1
                            ? {opacity}
                            : {opacity, minHeight: '122px'}
                    }
                    className={
                        currentItem.name === formConTextValue[index].name
                            ? `${styles.editBox} ${styles.editBoxActive}`
                            : styles.editBox
                    }
                    onClick={() => {
                        if (currentItem?.key && !currentItem?.label) {
                            message.error('请填写控件名称');
                            return;
                        }
                        setCurrentItem(formConTextValue[index]);
                        setCurrentIndex(index);
                    }}>
                    {/* 字段名称 */}
                    <div className={layout==="vertical"?styles.fieldNameAb:styles.fieldName}>
                        {formConTextValue[index].name}
                    </div>
                    {/* 传入的表单项 */}
                    {children}
                    {/* 当前选中才展示删除按钮 */}
                    {currentItem.name === formConTextValue[index].name && (
                        <div className={styles.editButton}>
                            <Popconfirm
                                title="确认要删除吗？"
                                placement="top"
                                onConfirm={(e) => {
                                    // 阻止事件冒泡，因为父div绑定了点击事件
                                    e?.stopPropagation();
                                    const cloneValue = [...formConTextValue];
                                    cloneValue.splice(index, 1);
                                    setFormConTextValue(cloneValue);
                                    setCurrentIndex(null);
                                    setCurrentItem({});
                                }}
                                okText="是"
                                cancelText="否">
                                <span>
                                    <DeleteFilled />
                                </span>
                            </Popconfirm>
                        </div>
                    )}
                </div>
            )}
            {!onlyShowForm && !checkForm && !isEditing && (
                <div
                    onClick={() => {
                        setCurrentItem(formConTextValue[index]);
                        setCurrentIndex(index);
                    }}>
                    {children}
                </div>
            )}

            {/* 只展示form */}
            {onlyShowForm && children}

            {/* checkForm */}
            {checkForm && (
                <div style={{display: 'flex'}}>
                    <Checkbox
                        style={{marginTop: '7px'}}
                        checked={!formConTextValue[index].disabled}
                        onChange={(e) => {
                            const cloneValue = cloneDeep(formConTextValue);
                            cloneValue[index].disabled = !e.target.checked;
                            setFormConTextValue(cloneValue);
                        }}
                    />
                    <span
                        style={{
                            flex: '1',
                        }}>
                        {children}
                    </span>
                </div>
            )}
        </Col>
    );
};

export default EditBox;
